/*
 * Filename TestOrderServiceImpl.java 2022年03月10日
 * Copyright © Ehome Co.Ltd. All Rgiths Reserved.
 * @author wuyingchao
 */
package com.ehome.poct.core.service.order;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ehome.core.service.AbstractCrudServiceImpl;
import com.ehome.exception.BizCheckedException;
import com.ehome.exception.BizException;
import com.ehome.lock.LockInfo;
import com.ehome.lock.LockTemplate;
import com.ehome.paymentcenter.api.pay.BaseQueryReqCO;
import com.ehome.paymentcenter.api.pay.constant.PayStatus;
import com.ehome.paymentcenter.api.pay.notify.BaseNotifyRspCO;
import com.ehome.paymentcenter.api.pay.qrcode.QrCodeRspCO;
import com.ehome.poct.base.api.dto.doctor.ClinicDTO;
import com.ehome.poct.base.api.enums.BusinessOrderTypeEnums;
import com.ehome.poct.core.api.constant.enums.CheckOrderStatus;
import com.ehome.poct.core.api.constant.enums.OrderType;
import com.ehome.poct.core.api.constant.enums.ScanTypeEnums;
import com.ehome.poct.core.api.dto.check.CheckProjectDTO;
import com.ehome.poct.core.api.dto.check.CheckStatusDTO;
import com.ehome.poct.core.api.dto.check.TestRecordDetailDTO;
import com.ehome.poct.core.api.dto.check.TestUniRecordDTO;
import com.ehome.poct.core.api.dto.check.gold.TestConfPoctStripTypeDTO;
import com.ehome.poct.core.api.dto.order.PrePlaceOrderDTO;
import com.ehome.poct.core.api.dto.order.TestOrderCheckRecordDTO;
import com.ehome.poct.core.api.dto.order.TestOrderDTO;
import com.ehome.poct.core.api.dto.order.TestOrderItemDTO;
import com.ehome.poct.core.api.dto.report.ProfitDTO;
import com.ehome.poct.core.api.qry.StripPriceProfitQry;
import com.ehome.poct.core.api.virtual.TestOrderInternalPayService;
import com.ehome.poct.core.api.virtual.TestOrderService;
import com.ehome.poct.core.api.vo.order.OrderItemPriceVO;
import com.ehome.poct.core.api.vo.order.OrderStatusVO;
import com.ehome.poct.core.api.vo.order.QrCodeVO;
import com.ehome.poct.core.api.vo.order.UnificationPayVO;
import com.ehome.poct.core.constant.OrderBizCodeEnums;
import com.ehome.poct.core.mapping.TestOrderMapping;
import com.ehome.poct.core.mapping.TestStripMapping;
import com.ehome.poct.core.properties.PayProperties;
import com.ehome.poct.core.repository.*;
import com.ehome.poct.core.repository.check.TestUniRecordRepository;
import com.ehome.poct.core.repository.order.TestOrderCheckRecordRepository;
import com.ehome.poct.core.repository.order.TestOrderItemRepository;
import com.ehome.poct.core.repository.order.TestOrderRepository;
import com.ehome.poct.core.service.CheckEventServiceImpl;
import com.ehome.poct.core.service.rpc.CorePayClient;
import com.ehome.poct.core.utils.EHomeAssert;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author wyc
 * @since 2022/03/10 11:41
 **/
@Service
@AllArgsConstructor
@Slf4j
public class TestOrderServiceImpl extends AbstractCrudServiceImpl<TestOrderDTO> implements TestOrderService {

    public static final String ORDER_KEY_PREFIX = "client_test_order:";
    public static final String PAY_KEY_PREFIX = "client_test_pay_info:";

    private final TestOrderRepository orderRepository;
    private final TestOrderCheckRecordRepository testOrderCheckRecordRepository;
    private final TestOrderItemRepository itemRepository;
    private final TestReportPoctRepository reportPoctRepository;
    private final TestReportPoctUniRepository reportPoctUniRepository;
    private final BldReportRepository bldReportRepository;
    private final TestUniRecordRepository uniRecordRepository;
    private final CorePayClient payClient;
    private final PayProperties payProperties;
    private final TestOrderMapping orderMapping;
    private final RedisTemplate redisTemplate;
    private final TestConfPoctStripTypeRepository stripTypeRepository;
    private final TestReportDataConfigRepository testReportDataConfigRepository;
    private final Map<String, String> reportName = Map.ofEntries(
            Map.entry("U_CRP_SAA_REPORT", "CRP+SAA检测"),
            Map.entry("S_CRP_SAA_REPORT", "CRP+SAA双联试纸检测"),
            Map.entry("U_EV71IGM_CVA16IGM_REPORT", "手足口检测"),
            Map.entry("U_MPIGM_CPNIGM_REPORT", "肺支肺衣联合检测"),
            Map.entry("U_MP_AB_CPNIGM_REPORT", "肺支总抗肺衣联合检测")
    );
    private final Integer TYPE121 = 121;
    private final Integer U_CRP_SAA_18 = 18;
    private final String MPIGM_REPORT = "MPIGM_REPORT";
    private final String MPAB_REPORT = "MPAB_REPORT";
    private final String CPNIGM_REPORT = "CPNIGM_REPORT";
    private final String U_MPIGM_CPNIGM_REPORT = "U_MPIGM_CPNIGM_REPORT";
    private final String U_MP_AB_CPNIGM_REPORT = "U_MP_AB_CPNIGM_REPORT";
    private final String CRP_SAA_BLOOD_NAME = "CRP+SAA+血常规联合检测";

    private final CheckProjectRepository checkProjectRepository;
    private final TestStripMapping testStripMapping;

    private final LockTemplate lockTemplate;

    @Autowired
    private CheckEventServiceImpl checkEventService;

    @Autowired
    private TestOrderCheckRecordRepository orderCheckRecordRepository;

    @Autowired
    private TestOrderInternalPayService orderInternalPayService;

    @Autowired
    private TestOrderCombinationServiceImpl combinationService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public QrCodeVO place(TestOrderDTO order, Boolean isPay) {
        EHomeAssert.isTrue(CollUtil.isNotEmpty(order.getItems()), OrderBizCodeEnums.ORDER_ITEM_IS_NULL);
        List<TestOrderItemDTO> orderItemDTOList = rebuildOrderItems(order.getItems(), order.getClinicId(), order.getTestOrderNo());
        order.setItems(orderItemDTOList);
        itemRepository.batchInsert(order.getItems());
        BigDecimal payMoney = order.getItems().stream().map(TestOrderItemDTO::getFee).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        order.setPayMoney(payMoney);
        order.setTotalMoney(payMoney);
        order.setStatus(PayStatus.INIT.getStatus());
        this.insert(order);

        UnificationPayVO unificationPayVO = orderInternalPayService.internalPay(order, ScanTypeEnums.C2B, null);
        if (isPay) {
            order.setStatus(PayStatus.SUCCESS.getStatus());
            this.update(order.getTestOrderId(), new TestOrderDTO().setIsPay(Boolean.TRUE).setStatus(PayStatus.SUCCESS.getStatus()));
            // 删除分账信息
            combinationService.getUnificationPayRepository().delShareAccountByOrderNo(order.getTestOrderNo());
            return new QrCodeVO().setOrderNo(order.getTestOrderNo()).setPayUrl("无需支付").setPromptMsg("无需支付");
        }

        ClinicDTO clinicDTO = combinationService.getClinicClient().getById(order.getClinicId());
        // todo check clinicDTO
        order.setAgentId(clinicDTO.getTermAgentId());
        saveRecordDetailsInfo(order, orderItemDTOList);

        QrCodeRspCO qrCodeRspCO = BeanUtil.copyProperties(unificationPayVO.getPayInfo(), QrCodeRspCO.class);
        // 3. 缓存订单
        final var qrCodeVO = orderMapping.to(qrCodeRspCO, order.getTotalMoney(), payProperties.getTimeout() * 60);
        cacheOrder(qrCodeVO, orderMapping.toStatus(order));
        this.update(order.getTestOrderId(), new TestOrderDTO().setOutOrderNo(qrCodeRspCO.getOutOrderNo()));
        return qrCodeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public QrCodeVO unionPlace(TestOrderDTO order, TestUniRecordDTO updateContent) {
        final var items = itemRepository.list(new TestOrderItemDTO().setTestOrderNo(order.getTestOrderNo()));
        final var itemPrices = this.getPriceItemByOrderItem(items);
        final var amounts = orderMapping.to(itemPrices);
        // 1. 修改联机记录
        uniRecordRepository.updateSelective(updateContent.getTestUniRecordId(), updateContent);
        // 2. 获取支付链接
        UnificationPayVO unificationPayVO = orderInternalPayService.internalPay(order, ScanTypeEnums.C2B, null);
        QrCodeRspCO qrCodeRspCO = BeanUtil.copyProperties(unificationPayVO.getPayInfo(), QrCodeRspCO.class);
        // 3. 更新外部订单号
        orderRepository.updateSelective(order.getTestOrderId(), new TestOrderDTO().setOutOrderNo(qrCodeRspCO.getOutOrderNo()));
        // 4. 缓存订单
        final var qrCodeVO = orderMapping.to(qrCodeRspCO, order.getTotalMoney(), payProperties.getTimeout() * 60);
        cacheOrder(qrCodeVO, orderMapping.toStatus(order));
        return qrCodeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void prePlace(TestOrderDTO order, TestUniRecordDTO updateContent) {
        List<Integer> stripCodeList = order.getItems().stream().map(TestOrderItemDTO::getStripTypeCode).filter(Objects::nonNull).collect(Collectors.toList());
        List<ProfitDTO> profitDTOList = orderInternalPayService.getPriceProfit(order.getClinicId(),stripCodeList);

        orderInternalPayService.saveShareAccount(order.getItems(), profitDTOList, order.getClinicId());
        // 1. 保存订单
        saveOrder(orderMapping.to(order, "", false));
        // 2. 修改联机记录
        uniRecordRepository.updateSelective(updateContent.getTestUniRecordId(), updateContent.setTestOrderNo(order.getTestOrderNo()));
        // 3. 缓存订单状态
        redisTemplate.opsForValue().set(ORDER_KEY_PREFIX + order.getTestOrderNo(), new OrderStatusVO(null, false, "未支付", 0), 150, TimeUnit.MINUTES);
    }

    @Override
    public QrCodeVO getPayLink(String orderNo) {
        // 1. 获取缓存中的订单支付链接
        final var obj = redisTemplate.opsForValue().get(PAY_KEY_PREFIX + orderNo);
        if (ObjectUtil.isNotNull(obj)) {
            final var expire = redisTemplate.boundValueOps(PAY_KEY_PREFIX + orderNo).getExpire();
            return JSONUtil.toBean(new JSONObject(obj), QrCodeVO.class).setExpireTime(expire);
        }
        // 2. 缓存中没有，则重新下单
        final var order = this.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        UnificationPayVO unificationPayVO = orderInternalPayService.internalPay(order, ScanTypeEnums.C2B, null);
        QrCodeRspCO qrCodeRspCO = BeanUtil.copyProperties(unificationPayVO.getPayInfo(), QrCodeRspCO.class);
        this.updateSelective(order.getTestOrderId(), new TestOrderDTO().setOutOrderNo(qrCodeRspCO.getOutOrderNo()));
        final var vo = orderMapping.to(qrCodeRspCO, order.getTotalMoney(), payProperties.getTimeout() * 60);
        cacheOrder(vo, orderMapping.toStatus(order));
        return vo;
    }

    @SuppressWarnings("unchecked")
    @Override
    public OrderStatusVO getOrderStatus(String orderNo) {
        LockInfo lockInfo = lockTemplate.lock(orderNo);
        if (Objects.isNull(lockInfo)) {
            OrderStatusVO orderStatusVO = new OrderStatusVO();
            orderStatusVO.setIsPay(false);
            return orderStatusVO;
        }
        try {
            // 1. 从缓存中查询
            final var obj = redisTemplate.opsForValue().get(ORDER_KEY_PREFIX + orderNo);
            if (ObjectUtil.isNotNull(obj)) {
                log.info("cache order info key:[{}] , value:[{}]", ORDER_KEY_PREFIX + orderNo, obj);
                return JSONUtil.toBean(new JSONObject(obj), OrderStatusVO.class);
            }
            // 2. 从数据库中查询
            final var order = this.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
            return Optional.ofNullable(order).map(o -> {
                log.info("order info orderNo:[{}] , isPay:[{}] , status:[{}]", o.getStatus(), o.getIsPay(), o.getStatus());
                final var statusVO = new OrderStatusVO(o.getPayType(), o.getIsPay(), payStatusToMsg(o.getStatus()), o.getStatus());
                // 查询结果缓存到Redis中
                redisTemplate.opsForValue().set(ORDER_KEY_PREFIX + o.getTestOrderNo(), JSONUtil.toJsonStr(o), 3, TimeUnit.SECONDS);
                return statusVO;
            }).orElse(new OrderStatusVO());
        } finally {
            lockTemplate.releaseLock(lockInfo);
        }
    }

    @Override
    public OrderStatusVO getOrderFromCenter(String orderNo) {
        final var order = this.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        // 1. 苏中订单的查询状态需特殊处理（必须是苏中订单，并且都是由优惠卷进行的支付）
        if (order.getSourceType().equals(10) && order.getPayMoney().signum() == 0 && order.getCouponMoney().signum() == 1) {
            return orderMapping.toStatus(order);
        }
        // 2. 查询支付中心订单状态
        final var notifyRspCO = feignResultDecode(payClient.qrCodeQuery(new BaseQueryReqCO(null, order.getOutOrderNo())), BaseNotifyRspCO.class);
        // 3. 修改订单信息(未支付的状态，不需要修改)
        if (!notifyRspCO.getPayStatus().equals(PayStatus.INIT)) {
//            orderCmdExe.completeOrder(notifyRspCO, order);
        }
        // 4. 返回订单状态
        return getOrderStatus(orderNo);
    }

    @Override
    public void delByOrderNo(String orderNo) {
//        redisTemplate.delete(ORDER_KEY_PREFIX + orderNo);
//        redisTemplate.delete(PAY_KEY_PREFIX + orderNo);
//        orderRepository.delByOrderNo(orderNo);
        final var order = orderRepository.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        //订单已支付，不做处理
        if (!PayStatus.SUCCESS.getStatus().equals(order.getStatus())) {
            reportPoctRepository.delByOrderNo(orderNo);
            reportPoctUniRepository.delByOrderNo(orderNo);
            bldReportRepository.delByOrderNo(orderNo);
            uniRecordRepository.delByOrderNo(orderNo);
        }
    }

    @Override
    public void updateOrderStatus(String orderNo) {
        final var orderDTO = selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        EHomeAssert.notNull(orderDTO, OrderBizCodeEnums.NOT_FOUND_TEST_ORDER, orderNo);
        EHomeAssert.isTrue(orderDTO.getIsPay(), OrderBizCodeEnums.ORDER_WAIT_PAY);

        boolean refundFlag = CheckOrderStatus.REFUND_SUCCESS.getCode().equals(orderDTO.getStatus())
                || CheckOrderStatus.REFUNDING.getCode().equals(orderDTO.getStatus());
        EHomeAssert.isTrue(!refundFlag, OrderBizCodeEnums.ORDER_REFUND_DISABLE_CHECK, orderNo);
        update(orderDTO.getTestOrderId(), orderDTO.setIsPay(true).setStatus(PayStatus.SUCCESS.getStatus()).setOrderType(OrderType.FREE.getType()).setPayMoney(BigDecimal.ZERO));
    }

    public void completeOrder(BaseNotifyRspCO callBack, TestOrderDTO order) {
        final var orderNo = callBack.getOrderNo();
//        log.info("支付中心回调，订单号：{}，支付状态：{}", orderNo, callBack.getPayStatus().getStatus());
        // 订单原始状态
        final var status = order.getStatus();
        // 幂等，状态没变化，不更新
        if (status.equals(callBack.getPayStatus().getStatus())) {
            return;
        }
        // 1. 修改订单状态
        reportPoctRepository.updateStatus(orderNo, callBack.getPayStatus().getStatus());
        reportPoctUniRepository.updateStatus(orderNo, callBack.getPayStatus().getStatus());
//        var isUpdate = orderService.updateAndCache(orderMapping.to(order, callBack), status);
//        // 2. 支付成功且订单修改成功，则完成回调
//        if (callBack.getPayStatus().equals(PayStatus.SUCCESS) && isUpdate) {
//            // 2.1 修改单项检测报告状态
//            var singleReport = orderMapping.toSigReport(callBack);
//            singleReport.update(Wrappers.<TestReportPoctDO>lambdaUpdate().eq(TestReportPoctDO::getPayOrderNo, orderNo));
//            // 2.2 修改联合检测报告状态
//            var unionReport = orderMapping.toUniReport(callBack);
//            unionReport.update(Wrappers.<TestReportPoctUniDO>lambdaUpdate().eq(TestReportPoctUniDO::getPayOrderNo, orderNo));
//            // 2.3 修改DDD版本血常规检测报告状态
//            bldReportService.lambdaUpdate().eq(BloodCheckReport::getOrderNo, orderNo)
//                    .set(BloodCheckReport::getPayStatus, callBack.getPayStatus().getStatus()).update();
//            // 2.4 对订单金额进行流转
//            profitRecordCmdExe.cashFlow(order);
//            // 2.5 发送风控事件
//            eventBus.publish(orderMapping.to(order));
//        }
    }


    /**
     * 保存订单信息
     *
     * @param order
     */
    private void saveOrder(TestOrderDTO order) {
        // 1. 保存订单
        this.insert(order);
        // 2. 保存订单项
        itemRepository.batchInsert(order.getItems());
    }

    /**
     * 缓存订单信息
     *
     * @param qrcode
     * @param status
     */
    private void cacheOrder(QrCodeVO qrcode, OrderStatusVO status) {
        // 1. 缓存订单内容
        redisTemplate.opsForValue().set(PAY_KEY_PREFIX + qrcode.getOrderNo(), qrcode, payProperties.getTimeout(), TimeUnit.MINUTES);
        // 2. 缓存订单状态
        redisTemplate.opsForValue().set(ORDER_KEY_PREFIX + qrcode.getOrderNo(), status, payProperties.getTimeout(), TimeUnit.MINUTES);
    }

    /**
     * 远程调用解码
     *
     * @param result
     * @param beanClass
     * @param <T>
     * @return
     */
    private <T> T feignResultDecode(String result, Class<T> beanClass) {
        final var resultObj = new JSONObject(result);
        final var bizCode = resultObj.getInt("busiCode");
        if (bizCode != 0) {
            throw new BizException(bizCode, resultObj.getStr("msg"));
        }
        return JSONUtil.toBean(resultObj.getJSONObject("data"), beanClass);
    }

    /**
     * 获取订单状态描述
     *
     * @param status
     * @return
     */
    private String payStatusToMsg(Integer status) {
        return Arrays.stream(PayStatus.values()).filter(i -> i.getStatus().equals(status)).map(PayStatus::getDesc).findFirst().orElse(null);
    }

    public List<OrderItemPriceVO> getOrderItemPriceInfo(String orderNo) {
        //拿到订单信息
        TestOrderDTO order = this.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        //拿到订单项
        TestOrderItemDTO tod = new TestOrderItemDTO();
        tod.setTestOrderNo(orderNo);
        List<TestOrderItemDTO> orderItems = itemRepository.list(tod);
        List<TestConfPoctStripTypeDTO> allTypes = stripTypeRepository.list();
        CheckProjectDTO bloodCheck = checkProjectRepository.selectByCode(100);
        allTypes.add(testStripMapping.to(bloodCheck));
        Map<String, String> reportcodeWithTypecodes = testReportDataConfigRepository.repoctCodeWithTypeCode();
        return analysisOrderInfo(orderItems, allTypes, reportcodeWithTypecodes);
    }

    @Override
    public List<OrderItemPriceVO> getPriceItemByOrderItem(List<TestOrderItemDTO> items) {
        final var allTypes = stripTypeRepository.list();
        allTypes.add(testStripMapping.to(checkProjectRepository.selectByCode(100)));
        final var dataConfMap = testReportDataConfigRepository.repoctCodeWithTypeCode();
        return analysisOrderInfo(items, allTypes, dataConfMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateByOrderNo(CheckStatusDTO checkStatusDTO) {
        //修改检测记录表状态 20:订单完成
        testOrderCheckRecordRepository.updateByOrderNo(checkStatusDTO);
        if (checkStatusDTO.getStatus() == 20) {
            //所有检测是否都已检测完
            var checkOrder = testOrderCheckRecordRepository.list(new TestOrderCheckRecordDTO().setOrderNo(checkStatusDTO.getOrderNo()).setStatus(0));
            if (checkOrder.size() > 0) {
                return;
            }
        }
        //修改订单状态
        orderRepository.updateByOrderNo(checkStatusDTO);
        sendCheckCompleteMessage(checkStatusDTO.getOrderNo());
    }

    @Override
    public TestOrderDTO getTestOrderByOrderNo(String orderNo) {
        return orderRepository.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
    }

    /**
     * 分析订单订单项 价格信息 是否需要展示优惠价格
     *
     * @param items
     * @param allTypes
     * @return
     */
    private List<OrderItemPriceVO> analysisOrderInfo(List<TestOrderItemDTO> items, List<TestConfPoctStripTypeDTO> allTypes, Map<String, String> reportcodeWithTypecodes) {
        List<OrderItemPriceVO> itemList = new ArrayList<>();
        // 获取联合检测code
        handleItem(items);
        var specialCpnMpabMpigm = specialHandleCpnMpIgmMpab(items, allTypes);
        if (null != specialCpnMpabMpigm) {
            items = items.stream().filter(i -> !List.of(CPNIGM_REPORT, MPAB_REPORT, MPIGM_REPORT, U_MPIGM_CPNIGM_REPORT, U_MP_AB_CPNIGM_REPORT).contains(i.getTestReportTypeCode())).collect(Collectors.toList());
            itemList.add(specialCpnMpabMpigm);
        }
        List<String> unionCode = items.stream().filter(i -> i.getTestPoctReportType() == 2).map(TestOrderItemDTO::getTestReportTypeCode).collect(Collectors.toList());
        // 如果订单中存在联合检测
        if (CollUtil.isNotEmpty(unionCode)) {
            List<TestOrderItemDTO> unionItems = items.stream().filter(i -> i.getTestPoctReportType() == 2).collect(Collectors.toList());
            List<TestOrderItemDTO> sigItems = items.stream().filter(i -> i.getTestPoctReportType() == 1).collect(Collectors.toList());

            List<Integer> stripCodes = testReportDataConfigRepository.getTypeCodes(unionCode);
            // 过滤掉组成了联合报告的单项检测，并分组 <单/联，报告码>
            Map<Integer, List<String>> reportCodesMap = items.stream().filter(i ->
                            !CollUtil.contains(stripCodes, i.getStripTypeCode()))
                    .collect(Collectors.groupingBy(TestOrderItemDTO::getTestPoctReportType,
                            Collectors.mapping(TestOrderItemDTO::getTestReportTypeCode, Collectors.toList())));
            // 单项检测报告
            if (CollUtil.isNotEmpty(reportCodesMap.get(1))) {
                List<String> sigList = reportCodesMap.get(1);
                //遍历订单项的单项内容
                sigItems.stream().filter(t -> sigList.contains(t.getTestReportTypeCode())).forEach(dto -> {
                    //拿到检测类型信息
                    var stripTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().toString().equals(reportcodeWithTypecodes.get(dto.getTestReportTypeCode()))).findFirst();
                    stripTypeDTO.ifPresent(i -> {
                        String enname = i.getName().replace("检测", "");
                        if (TYPE121.equals(i.getTypeCode())) {
                            enname = CRP_SAA_BLOOD_NAME;
                        }
                        itemList.add(new OrderItemPriceVO(1, enname,
                                "",
                                i.getPrice(),
                                dto.getFee(),
                                i.getShowOriginPrice(),
                                dto.getTestReportTypeCode()
                        ));
                    });

                });
            }
            // 联合检测报告
            if (CollUtil.isNotEmpty(reportCodesMap.get(2))) {
                List<String> uniList = reportCodesMap.get(2);
                unionItems.stream().filter(t -> uniList.contains(t.getTestReportTypeCode())).forEach(dto -> {
                    String stripeCodes = reportcodeWithTypecodes.get(dto.getTestReportTypeCode());
                    String[] list = stripeCodes.split(",");
                    //单独把联合的补上去
                    var unistrTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().equals(dto.getStripTypeCode())).findFirst();
                    Integer showUniPrice = unistrTypeDTO.map(TestConfPoctStripTypeDTO::getShowOriginPrice).orElse(0);
                    if (BigDecimal.ZERO.compareTo(dto.getFee()) >= 0) {
                        List<OrderItemPriceVO> tempResult = new ArrayList<>();
                        Arrays.stream(list).forEach(a -> {
                            var strTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().toString().equals(a)).findFirst();
                            if (TYPE121.equals(dto.getStripTypeCode())) {
                                strTypeDTO.ifPresent(i -> {
                                    String enname = CRP_SAA_BLOOD_NAME;
                                    tempResult.add(new OrderItemPriceVO(1, enname,
                                            i.getFullName(),
                                            i.getPrice(),
                                            dto.getFee(),
                                            i.getShowOriginPrice(),
                                            dto.getTestReportTypeCode()
                                    ));
                                });
                            } else {
                                var tempItem = sigItems.stream().filter(t -> t.getStripTypeCode().toString().equals(a)).findFirst();
                                tempItem.ifPresent(t -> {
                                    strTypeDTO.ifPresent(i -> {
                                        String enname = i.getName().replace("检测", "");
                                        if (TYPE121.equals(i.getTypeCode())) {
                                            enname = CRP_SAA_BLOOD_NAME;
                                        }
                                        tempResult.add(new OrderItemPriceVO(1, enname,
                                                i.getFullName(),
                                                i.getPrice(),
                                                t.getFee(),
                                                i.getShowOriginPrice(),
                                                dto.getTestReportTypeCode()
                                        ));
                                    });
                                });
                            }

                        });

                        itemList.add(buidItem(tempResult, dto.getTestReportTypeCode(), dto.getStripTypeCode(), null, showUniPrice, unistrTypeDTO));
                    } else {
                        List<OrderItemPriceVO> tempResult = new ArrayList<>();

                        Arrays.stream(list).forEach(
                                b -> {
                                    var strTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().toString().equals(b)).findFirst();
                                    if (TYPE121.equals(dto.getStripTypeCode())) {
                                        strTypeDTO.ifPresent(i -> {
                                            String enname = CRP_SAA_BLOOD_NAME;
                                            tempResult.add(new OrderItemPriceVO(1, enname,
                                                    i.getFullName(),
                                                    i.getPrice(),
                                                    dto.getFee(),
                                                    i.getShowOriginPrice(),
                                                    dto.getTestReportTypeCode()
                                            ));
                                        });
                                    } else if (U_CRP_SAA_18.equals(dto.getStripTypeCode())) {
                                        strTypeDTO.ifPresent(i -> {
                                            tempResult.add(new OrderItemPriceVO(1, i.getName().replace("检测", ""),
                                                    i.getFullName(),
                                                    i.getPrice(),
                                                    dto.getFee(),
                                                    showUniPrice,
                                                    dto.getTestReportTypeCode()
                                            ));
                                        });

                                    } else {
                                        var tempItem = sigItems.stream().filter(t -> t.getStripTypeCode().toString().equals(b)).findFirst();
                                        tempItem.ifPresent(t -> {
                                            strTypeDTO.ifPresent(i -> {
                                                String enname = i.getName().replace("检测", "");
                                                if (TYPE121.equals(i.getTypeCode())) {
                                                    enname = CRP_SAA_BLOOD_NAME;
                                                }
                                                tempResult.add(new OrderItemPriceVO(1, enname,
                                                        i.getFullName(),
                                                        i.getPrice(),
                                                        BigDecimal.valueOf(0.00),
                                                        i.getShowOriginPrice(),
                                                        dto.getTestReportTypeCode()

                                                ));
                                            });
                                        });
                                    }

                                }
                        );
                        itemList.add(buidItem(tempResult, dto.getTestReportTypeCode(), dto.getStripTypeCode(), dto.getFee(), showUniPrice, unistrTypeDTO));
                    }

                });

            }
            //只做121的
            if (CollUtil.isNotEmpty(reportCodesMap.get(3))) {
                List<TestOrderItemDTO> itemDTOS = items.stream().filter(it -> reportCodesMap.get(3).stream().anyMatch(re -> re.equals(it.getTestReportTypeCode()))).collect(Collectors.toList());
                itemDTOS.forEach(f -> {
                    var stripTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().equals(f.getStripTypeCode())).collect(Collectors.toList());
                    TestConfPoctStripTypeDTO testConfPoctStripTypeDTO = stripTypeDTO.get(0);
                    if (TYPE121.equals(f.getStripTypeCode())) {
                        itemList.add(new OrderItemPriceVO(1, f.getOrderItemName(),
                                "",
                                testConfPoctStripTypeDTO.getPrice(),
                                f.getFee(),
                                testConfPoctStripTypeDTO.getShowOriginPrice(),
                                f.getTestReportTypeCode()
                        ));
                    }
                });
            }
        }
        // 只有单项检测报告
        else {
            items.stream().forEach(dto -> {
                //拿到检测类型信息
                var stripTypeDTO = allTypes.stream().filter(t -> t.getTypeCode().toString().equals(reportcodeWithTypecodes.get(dto.getTestReportTypeCode()))).findFirst();
                stripTypeDTO.ifPresent(i -> {
                    String enname = i.getName().replace("检测", "");
                    if (TYPE121.equals(i.getTypeCode())) {
                        enname = CRP_SAA_BLOOD_NAME;
                    }
                    itemList.add(new OrderItemPriceVO(1, enname,
                            "",
                            i.getPrice(),
                            dto.getFee(),
                            i.getShowOriginPrice(),
                            dto.getTestReportTypeCode()
                    ));
                });
            });
        }
        return groupItemResult(itemList);
    }


    private OrderItemPriceVO buidItem(List<OrderItemPriceVO> sigleItems, String reportcode, Integer stripTypeCode, BigDecimal uniSalesPrice, Integer uniPriceShowOffMoney, Optional<TestConfPoctStripTypeDTO> unistrTypeDTO) {
        BigDecimal totalOriginPrice = sigleItems.stream().map(OrderItemPriceVO::getOriginPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (U_CRP_SAA_18.equals(stripTypeCode) && unistrTypeDTO.isPresent()) {
            totalOriginPrice = unistrTypeDTO.get().getPrice();
        }
        BigDecimal totalSalesPrice = uniSalesPrice == null ? (sigleItems.stream().map(OrderItemPriceVO::getSalesPrice).reduce(BigDecimal.ZERO, BigDecimal::add)) : uniSalesPrice;
        String enname = sigleItems.stream().map(OrderItemPriceVO::getStripNameEn).collect(Collectors.joining("+"));
        String cnname = reportName.get(reportcode);
        Integer show = sigleItems.stream().map(OrderItemPriceVO::getInShowOfferMoney).reduce(0, Integer::sum) + uniPriceShowOffMoney;
        return new OrderItemPriceVO(2, enname, cnname, totalOriginPrice, totalSalesPrice, show, reportcode);
    }

    private void handleItem(List<TestOrderItemDTO> items) {
        items.forEach(i ->
                {
                    if (Integer.valueOf(100).equals(i.getStripTypeCode())) {
                        i.setTestReportTypeCode("R_REPORT");
                    }
                    if (Integer.valueOf(121).equals(i.getStripTypeCode())) {
                        i.setTestReportTypeCode("U_CRP_SAA_BLD_REPORT");
                    }
                }
        );
    }

    /**
     * 对已得出对订单项 显示单价 做一次分组
     *
     * @param items
     * @return
     */
    private List<OrderItemPriceVO> groupItemResult(List<OrderItemPriceVO> items) {
        //如果是单测的 不显示价格的 分组到一起
        List<OrderItemPriceVO> result = new ArrayList<>();
        result.addAll(items.stream().filter(t -> t.getOrderItemType() == 2).collect(Collectors.toList()));
        result.addAll(items.stream().filter(t -> t.getOrderItemType() == 1 && t.getShowOfferMoney() && BigDecimal.ZERO.compareTo(t.getSalesPrice()) <= 0).collect(Collectors.toList()));
        var ot = groupSingleItem(items.stream().filter(t -> t.getOrderItemType() == 1 && !t.getShowOfferMoney()).collect(Collectors.toList()));
        if (null != ot) {
            result.add(ot);
        }
        return result;
    }

    /**
     * 构建单测 不显示价格的 群组分类
     *
     * @param sigleItems
     * @return
     */
    private OrderItemPriceVO groupSingleItem(List<OrderItemPriceVO> sigleItems) {
        if (CollUtil.isNotEmpty(sigleItems)) {
            BigDecimal totalOriginPrice = sigleItems.stream().map(OrderItemPriceVO::getOriginPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal totalSalesPrice = sigleItems.stream().map(OrderItemPriceVO::getSalesPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            String enname = sigleItems.stream().map(OrderItemPriceVO::getStripNameEn).collect(Collectors.joining("，"));
            Integer show = 0;
            return new OrderItemPriceVO(1, enname, null, totalOriginPrice, totalSalesPrice, show, "");
        }
        return null;
    }

    /**
     * 特殊分组 对肺只肺衣 肺只总抗 特殊处理 合成一个
     *
     * @param orderItems
     * @return
     */
    private OrderItemPriceVO specialHandleCpnMpIgmMpab(List<TestOrderItemDTO> orderItems, List<TestConfPoctStripTypeDTO> allTypes) {
        //如果同时存在 cpn mpigm mpab
        var oCpn = orderItems.stream().filter(t -> CPNIGM_REPORT.equals(t.getTestReportTypeCode())).findFirst();
        if (oCpn.isEmpty()) {
            return null;
        }

        var oMpigm = orderItems.stream().filter(t -> MPIGM_REPORT.equals(t.getTestReportTypeCode())).findFirst();
        if (oMpigm.isEmpty()) {
            return null;
        }

        var oMpab = orderItems.stream().filter(t -> MPAB_REPORT.equals(t.getTestReportTypeCode())).findFirst();
        if (oMpab.isEmpty()) {
            return null;
        }

        var cpn = oCpn.get();
        var mpigm = oMpigm.get();
        var mpab = oMpab.get();
        BigDecimal totalOriginPrice = allTypes.stream().filter(i -> List.of(cpn.getStripTypeCode(), mpab.getStripTypeCode(), mpigm.getStripTypeCode()).contains(i.getTypeCode()))
                .map(TestConfPoctStripTypeDTO::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalSalesPrice = orderItems.stream().filter(i -> List.of(cpn.getTestReportTypeCode(), mpab.getTestReportTypeCode(), mpigm.getTestReportTypeCode(), U_MP_AB_CPNIGM_REPORT, U_MPIGM_CPNIGM_REPORT).contains(i.getTestReportTypeCode()))
                .map(TestOrderItemDTO::getFee).reduce(BigDecimal.ZERO, BigDecimal::add);
        String enname = allTypes.stream().filter(i -> List.of(cpn.getStripTypeCode(), mpab.getStripTypeCode(), mpigm.getStripTypeCode()).contains(i.getTypeCode()))
                .map(i -> i.getName().replace("检测", "")).collect(Collectors.joining("，"));
        Integer show = allTypes.stream().filter(i -> List.of(cpn.getStripTypeCode(), mpab.getStripTypeCode(), mpigm.getStripTypeCode()).contains(i.getTypeCode()))
                .map(TestConfPoctStripTypeDTO::getShowOriginPrice).reduce(0, Integer::sum);
        String cnname = allTypes.stream().filter(i -> List.of(cpn.getStripTypeCode(), mpab.getStripTypeCode(), mpigm.getStripTypeCode()).contains(i.getTypeCode()))
                .map(i -> i.getFullName().replace("检测", "")).collect(Collectors.joining("，"));
        return new OrderItemPriceVO(1, enname, cnname, totalOriginPrice, totalSalesPrice, show, "");
    }


    /**
     * 检测完成后发送mq消息
     *
     * @param orderNo
     */
    private void sendCheckCompleteMessage(String orderNo) {
        TestOrderDTO testOrderDTO = this.selectOne(new TestOrderDTO().setTestOrderNo(orderNo));
        EHomeAssert.notNull(testOrderDTO, OrderBizCodeEnums.NOT_FOUND_TEST_ORDER, orderNo);
        if (!BusinessOrderTypeEnums.PRE_TRADE.equals(testOrderDTO.getBusinessOrderType())) {
            log.info("非支付前置订单 不执行下面操作");
            return;
        }
        List<TestOrderCheckRecordDTO> waitCheckRecordList = orderCheckRecordRepository.list(new TestOrderCheckRecordDTO().setOrderNo(orderNo).setStatus(0));
        if (CollUtil.isNotEmpty(waitCheckRecordList)) {
            log.info("存在待检测项 跳过本次操作 orderNo:[{}]", orderNo);
            return;
        }
        List<TestOrderDTO> testOrderDTOList = this.list(new TestOrderDTO().setTestOrderNo(orderNo));
        EHomeAssert.isTrue(CollUtil.isNotEmpty(testOrderDTOList), OrderBizCodeEnums.NOT_FOUND_TEST_ORDER, orderNo);
        checkEventService.sendCheckEvent(testOrderDTOList.get(0));
    }

    /**
     * 保存检查记录信息
     *
     * @param order
     * @param orderItemDTOList
     */
    private void saveRecordDetailsInfo(TestOrderDTO order, List<TestOrderItemDTO> orderItemDTOList) {
        final List<TestRecordDetailDTO> recordDtls = getRecordDetailList(order, orderItemDTOList);
        combinationService.getRecordDetailService().saveRecord(recordDtls);
    }

    private List<TestRecordDetailDTO> getRecordDetailList(TestOrderDTO order, List<TestOrderItemDTO> orderItemDTOList) {
        PrePlaceOrderDTO placeOrderDTO = combinationService.getOrderMapping().toPreOrder(order);
        return orderInternalPayService.convertRecordDetailList(placeOrderDTO, orderItemDTOList);
    }

    /**
     * 重新构建子订单项
     *
     * @param itemDTOList
     * @param clinicId
     * @param orderNo
     * @return
     */
    private List<TestOrderItemDTO> rebuildOrderItems(List<TestOrderItemDTO> itemDTOList, Long clinicId, String orderNo) {
        EHomeAssert.isTrue(CollUtil.isNotEmpty(itemDTOList), OrderBizCodeEnums.ORDER_ITEM_NO_IS_NULL);
        //根据检测项获取检查信息及价格方案
        List<Integer> stripCodeList = itemDTOList.stream().map(TestOrderItemDTO::getStripTypeCode).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isEmpty(stripCodeList)) {
            return itemDTOList;
        }

        List<TestOrderItemDTO> orderItemDTOList = orderInternalPayService.convertOrderItems(clinicId, stripCodeList, orderNo);
        if (CollUtil.isEmpty(orderItemDTOList)) {
            return itemDTOList;
        }

        Map<Integer, TestOrderItemDTO> inputOrderItemMap = itemDTOList.stream().collect(Collectors.toMap(TestOrderItemDTO::getStripTypeCode, orderItem -> orderItem, (o1, o2) -> o1));
        orderItemDTOList.forEach(orderItem -> {
            TestOrderItemDTO inputOrderItem = inputOrderItemMap.get(orderItem.getStripTypeCode());
            if (Objects.isNull(inputOrderItem)) {
                return;
            }
            orderItem.setTestPoctReportId(inputOrderItem.getTestPoctReportId());
            // 存在就不要覆盖了 获取活动价格的时候没有返回血常规的报告编码  所以这里特殊判断一下
            if (StrUtil.isBlank(orderItem.getTestReportTypeCode())) {
                orderItem.setTestReportTypeCode(inputOrderItem.getTestReportTypeCode());
            }
        });

        List<TestOrderItemDTO> remainingOrderItemList = itemDTOList.stream().filter(orderItem -> Objects.isNull(orderItem.getStripTypeCode())).collect(Collectors.toList());
        orderItemDTOList.addAll(remainingOrderItemList);
        return orderItemDTOList;
    }
}